﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GcmpModelInterface.h"
#include "IElement.h"

namespace gcmp {

    class IDocument;
    class IGroupType;
    class IModelView;

    /// \brief 组内元素的操作类型
    ///
    enum class GroupEditType : int32_t
    {
        AddElement = 0,         ///< 添加元素到组
        ChangeElement = 1,      ///< 修改组内元素
        DeleteElement = 2,      ///< 删除组内元素
        RemoveElement = 3,      ///< 将组内元素移出组
        RestoreReferenceElement = 4      ///< 恢复组类型元素的添加
    };


    /// \brief 组
    FINAL_INTERFACE class GCMP_MODEL_INTERFACE_EXPORT IGroup : public IElement
    {
        DEFINE_CAST_DERIVED(gcmp::IGroup, gcmp::IElement);
    public:

        /// \brief 为指定对象集合创建一个组
        /// \param pDocument 组所属的文档
        /// \param pModelView 视图指针，用于计算构件包围盒
        /// \param typeName 组类型名称
        /// \param elementIds 要添加到组的Elements的Id
        /// \return IGroup* 组对象的指针，失败时返回nullptr
        /// \note 在创建组之前会先创建组对应的组类型，然后再创建组
        static IGroup* Create(IDocument* pDocument, const IModelView* pModelView,
                const std::wstring& typeName, const UnorderedElementIdSet& elementIds);

        /// \brief 根据组类型创建组
        /// \param pDocument 组所属的文档
        /// \param pModelView 视图指针，用于计算构件包围盒
        /// \param groupTypeId 组类型的ElementId
        /// \return IGroup* 组对象的指针，失败时返回nullptr
        static IGroup* Create(IDocument* pDocument, const IModelView* pModelView, ElementId& groupTypeId);

        /// \brief 解除组的成组状态
        /// \param pDocument 组所属的文档
        /// \param groupId 组的ElementId
        /// \return 如果成功解除成组状态返回true，否则返回false
        static bool UnGroup(IDocument* pDocument, const ElementId& groupId);

        /// \brief 将元素添加到组并更新组类型和其他组
        /// \param elementId 要添加到组的元素的ElementId
        /// \return 添加成功返回true，否则返回false
        virtual bool AddElement(const ElementId& elementId) = 0;

        /// \brief 将元素从组中删除并更新组实例
        /// \param elementId 要删除的元素的ElementId
        /// \return 删除成功返回true，否则返回false
        virtual bool DeleteElement(const ElementId& elementId) = 0;

        /// \brief  将元素从组中移除并更新组实例
        /// \param elementId 要移除的元素的ElementId
        /// \return 移除成功返回true，否则返回false
        virtual bool RemoveElement(const ElementId& elementId) = 0;

        /// \brief 通知组对组中的元素做了修改
        /// \param elementId 修改了的元素的ElementId
        /// \return 修改成功返回true，否则返回false
        virtual bool ChangeElement(const ElementId& elementId) = 0;

        /// \brief 将对组的所有修改同步到族类型和其他组上面
        /// \return 同步成功返回true，同步失败返回false
        virtual bool Sync() = 0;

        /// \brief 获取组中element的个数
        /// \return 返回组内包含的element的个数
        virtual size_t GetElementsCount() const = 0;

        /// \brief 获取组中所有ElementIds
        /// \param [out] elementIds  组中所有元素的ElementId的集合
        /// \return 
        virtual void GetAllElementIdsInGroup(UnorderedElementIdSet& elementIds) const = 0;

        /// \brief 获取编辑模式下被修改数据的信息
        /// \return 返回被修改数据的ElementId和具体的操作
        virtual std::map<ElementId, GroupEditType> GetEditDataMap() const = 0;

        /// \brief 获取组中（包括嵌套组）所有ElementIds
        /// \param [out] elementIds 组中所有元素的ElementId的集合,包括所有递归组中的元素
        /// \param [in] depth 递归深度，等于0时与GetAllElementIdsInGroup一致，小于0时递归查询所有嵌套组中元素
        /// \return 
        virtual void GetAllElementIdsInGroupRecursively(UnorderedElementIdSet& elementIds, int depth) const = 0;

        /// \brief 获取GroupType和Group元素对应集合
        /// \return 返回GroupType和Group元素对应集合
        virtual const std::map<ElementId, ElementId>& GetTypeToGroupIdMap() const = 0;

        /// \brief 获取Group和GroupType元素对应集合
        /// \return 返回Group和GroupType元素对应集合
        virtual const std::map<ElementId, ElementId>& GetGroupToTypeIdMap() const = 0;

        /// \brief 获取GroupType和Group中引用元素对应集合
        /// \return 返回GroupType和Group中引用元素对应集合
        virtual const std::map<ElementId, ElementId>& GetReferenceTypeToGroupIdMap() const = 0;

        /// \brief 获取Group和GroupType中引用元素对应集合
        /// \return 返回Group和GroupType中引用元素对应集合
        virtual const std::map<ElementId, ElementId>& GetReferenceGroupToTypeIdMap() const = 0;

        /// \brief 设置组内元素是否可拾取
        /// \param isSnapble 可拾取为true，不可拾取为false
        /// \return 
        /// \note 当IGroupSetting中为ture时，该设置无效，IGroupSetting中为false时，该值才会生效
        virtual void SetSubElementSnapble(bool isSnapble) = 0;

        /// \brief 获取组内元素是否可拾取
        ///
        /// \return 组内元素可拾取返回true，否则返回false
        virtual bool IsSubElementSnapble() const = 0;

        /// \brief 判断元素是否在当前编辑的组中(只查找当前组的直接子元素)
        /// \param elementId 需要判断的元素
        /// \return 是组内元素true，否则返回false
        virtual bool IsElementInGroup(const ElementId& elementId) const = 0;

        /// \brief 判断元素是否在当前编辑的组中(遍历组内所有元素，包括嵌套组实例内的元素)
        /// \param elementId 需要判断的元素
        /// \return 是组内元素true，否则返回false
        virtual bool IsElementInGroupRecursively(const ElementId& elementId) const = 0;

        /// \brief 判断元素是否是当前组的引用元素
        /// \param elementId 需要判断的元素
        /// \return 是组内引用元素true，否则返回false
        virtual bool IsReferenceElementInGroup(const ElementId& elementId) const = 0;

        /// \brief 获取组类型到组的变换矩阵
        /// \return 返回变换矩阵
        /// \note 组的基准点是可以修改的，通过IGroupType和IGroup的IElementPosition计算得来的transform可能会有一些偏移，使用该接口会更准确
        virtual const Matrix4d& GetTypeToGroupTransform() const = 0;

        /// \brief 获取组关联更新数据的唯一标识
        ///
        /// \return 返回组关联更新数据的唯一标识
        virtual RegenDataId GetRegenerationRdId() const = 0;

#pragma region ObsoleteAPI
        /// \brief 为指定对象集合创建一个组
        /// \param pDocument 组所属的文档
        /// \param pModelView 视图指针，用于计算构件包围盒
        /// \param typeName 组类型名称
        /// \param elementIds 要添加到组的Elements的Id
        /// \param errorMsg 返回创建组的错误信息，如果用户不想获取具体的错误信息，可设为nullptr
        /// \return IGroup* 组对象的指针，失败时返回nullptr
        /// \note 在创建组之前会先创建组对应的组类型，然后再创建组
        GCMP_OBSOLETE_API(2021 / 09 / 01, IGroup::Create(IDocument*, const IModelView*, const std::wstring&,
            const UnorderedElementIdSet&)) static IGroup* Create(IDocument* pDocument, const IModelView* pModelView,
                const std::wstring& typeName, const UnorderedElementIdSet& elementIds, std::wstring* errorMsg);

        /// \brief 根据组类型创建组
        /// \param pDocument 组所属的文档
        /// \param pModelView 视图指针，用于计算构件包围盒
        /// \param groupTypeId 组类型的ElementId
        /// \param errorMsg 返回创建组的错误信息，如果用户不想获取具体的错误信息，可设为nullptr
        /// \return IGroup* 组对象的指针，失败时返回nullptr
        GCMP_OBSOLETE_API(2021 / 09 / 01, IGroup::Create(IDocument*, const IModelView*, ElementId&)) static IGroup* Create(IDocument* pDocument, 
            const IModelView* pModelView, ElementId& groupTypeId, std::wstring* errorMsg);
#pragma endregion ObsoleteAPI

        public:
            /// \brief 获取Implementation Uid
            ///
            /// \return UniIdentity 返回ImplementationUid
            static UniIdentity GetImplementationUid();

        virtual ~IGroup();
    };
}// namespace gcmp
